#include "comment_redis_adapter.h"

#include <map>
#include <thrift/Thrift.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TBufferTransports.h>

#include "base/logging.h"
// #include "redis_executor/scoped_reply.h"

using namespace std;
using namespace boost;
using namespace base;
using namespace apache::thrift;

namespace jztzp {

int RedisAdapter::CommentAdd(const CommentItem& comment) {
  const int MAX_BUF_LEN = 64;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  {
    // serialize
    shared_ptr<transport::TTransport> transport(new transport::TMemoryBuffer(512));
    shared_ptr<protocol::TProtocol> protocol(new protocol::TBinaryProtocol(transport));
    comment.write(protocol.get());

    snprintf(cmd_buf, MAX_BUF_LEN - 1, "SET comment:body:%lld %%b", comment.id);
    uint8_t * serialized;
    uint32_t size;
    (dynamic_cast<transport::TMemoryBuffer*>(transport.get()))->getBuffer(&serialized, &size);

    ScopedReply reply(redis_exec_.Execute("", cmd_buf, serialized, size));
    if (!reply) {
      LOG_ERROR("set comment body error, id=" << comment.id);
      return -1;
    } else {
      LOG_INFO("set comment body ok, id=" << comment.id);
    }
  }

  {
    // TODO: 先从db加载, 否则cache中只有一部分
    snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZADD comment:idx_by_article_id:%d %lld %lld", 
        comment.article_id, comment.id, comment.id);

    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_INTEGER) {
         LOG_DEBUG(cmd_buf << " success, reply=" << reply->integer);
      } else {
         LOG_WARN(cmd_buf << " bad reply type, type=" << reply->type);
      }
    } else {
      LOG_WARN(cmd_buf << ", null reply, may be connection err.");
    }
  }

  {
    // TODO: 先从db加载, 否则cache中只有一部分
    snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZADD comment:idx_by_sender_id:%d %lld %lld", 
        comment.sender_id, comment.id, comment.id);

    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_INTEGER) {
         LOG_DEBUG(cmd_buf << " success, reply=" << reply->integer);
      } else {
         LOG_WARN(cmd_buf << " bad reply type, type=" << reply->type);
      }
    } else {
      LOG_WARN(cmd_buf << ", null reply, may be connection err.");
    }
  }

  return 0;
}

int RedisAdapter::CommentRemove(int64_t id, int32_t article_id, int32_t sender_id) {
  const int MAX_BUF_LEN = 64;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN - 1] = '\0';

  {
    snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZREM comment:idx_by_sender_id:%d %lld", 
        sender_id, id);

    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_INTEGER) {
         LOG_DEBUG(cmd_buf << " success, reply=" << reply->integer);
      } else {
         LOG_WARN(cmd_buf << " bad reply type, type=" << reply->type);
         return -1;
      }
    } else {
      LOG_WARN(cmd_buf << ", null reply, may be connection err.");
      return -1;
    }
  }

  {
    snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZREM comment:idx_by_article_id:%d %lld", 
        article_id, id);

    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_INTEGER) {
         LOG_DEBUG(cmd_buf << " success, reply=" << reply->integer);
      } else {
         LOG_WARN(cmd_buf << " bad reply type, type=" << reply->type);
         return -2;
      }
    } else {
      LOG_WARN(cmd_buf << ", null reply, may be connection err.");
      return -2;
    }
  }

  return 0;
}

int RedisAdapter::CommentCountByArticle(int32_t article_id) {
  const int MAX_BUF_LEN = 64;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN] = '\0';

  snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZCOUNT comment:idx_by_article_id:%d -inf +inf", article_id);

  ScopedReply reply(redis_exec_.Execute("", cmd_buf));
  if (reply && reply->type == REDIS_REPLY_INTEGER) {
     LOG_DEBUG(cmd_buf << " success, reply-count=" << reply->integer);
     return reply->integer;
  }
  LOG_WARN(cmd_buf << " err.");
  return -1;
}

int RedisAdapter::CommentGetByArticle(CommentList * comments, int32_t article_id, int32_t offset, int32_t limit) {
  const int MAX_BUF_LEN = 64;
  char cmd_buf[MAX_BUF_LEN];
  cmd_buf[MAX_BUF_LEN] = '\0';

  std::string mget_cmd("MGET");
  {
    snprintf(cmd_buf, MAX_BUF_LEN - 1, "ZRANGE comment:idx_by_article_id:%d %d %d", 
        article_id, offset, offset + (limit - 1));

    ScopedReply reply(redis_exec_.Execute("", cmd_buf));
    if (reply) {
      if (reply->type == REDIS_REPLY_ARRAY) {
         LOG_DEBUG(cmd_buf << " comment count=" << reply->elements);
         if (reply->elements == 0) {
           return 0;
         }
         for(int i = 0; i < reply->elements; ++i) {
           redisReply * el = reply->element[i];
           mget_cmd += " comment:body:";
           mget_cmd += el->str;
           LOG_DEBUG("comment id" << i << "=" << el->str);
         }
      } else {
        LOG_WARN(cmd_buf << " bad reply type=" << reply->type);
        return -1;
      }
    } else {
      LOG_ERROR(cmd_buf << " null reply, may be connection err.");
      return -1;
    }
  }

  {
    ScopedReply reply(redis_exec_.Execute("", mget_cmd.c_str()));
    if (reply) {
      if (reply->type == REDIS_REPLY_ARRAY) {
         LOG_DEBUG(mget_cmd << ", reply array size :" << reply->elements);
         for(int i = 0; i < reply->elements; ++i) {
           redisReply * el = reply->element[i];
           if (el->type == REDIS_REPLY_STRING) {
             // deserialize
             shared_ptr<transport::TTransport> transport(new transport::TMemoryBuffer((uint8_t*)el->str, el->len));
             shared_ptr<protocol::TProtocol> protocol(new protocol::TBinaryProtocol(transport));

             comments->push_back(CommentItem());
             comments->back().read(protocol.get());
             LOG_DEBUG("id=" << comments->back().id << " content=" << comments->back().content);
           }
         }
      } else {
         LOG_WARN(mget_cmd << " bad reply type=" << reply->type);
         return -1;
      }
    } else {
      LOG_ERROR(mget_cmd << " null reply, may be connection err.");
      return -1;
    }
  }
  
  return 0;
}

}

